package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestEquivalencePredicatesIntegration(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		// --- Object Identity: eq? ---
		// eq? tests if two objects are the exact same object in memory.
		{"eq? with booleans", "(eq? #t #t)", "#t"}, // #t and #f are usually unique objects
		{"eq? with different booleans", "(eq? #t #f)", "#f"},
		{"eq? with numbers", "(eq? 1 1)", "#f"}, // Two '1's are different objects
		{"eq? with different numbers", "(eq? 1 2)", "#f"},

		// --- Value Equality for Primitives: eqv? ---
		// eqv? tests if two primitive values are "the same".
		{"eqv? with booleans", "(eqv? #t #t)", "#t"},
		{"eqv? with different booleans", "(eqv? #t #f)", "#f"},
		{"eqv? with numbers", "(eqv? 1 1)", "#t"},
		{"eqv? with rationals", "(eqv? 1/2 2/4)", "#t"}, // Values are equal when simplified
		{"eqv? with characters", "(eqv? #\\a #\\a)", "#t"},
		{"eqv? with strings", "(eqv? \"hello\" \"hello\")", "#t"}, // Strings with same content

		// --- Deep Structural Equality: equal? ---
		// equal? recursively compares the structure and contents of compound data.
		{"equal? with booleans", "(equal? #t #t)", "#t"},
		{"equal? with different booleans", "(equal? #t #f)", "#f"},
		{"equal? with numbers", "(equal? 1 1)", "#t"},
		{"equal? with rationals", "(equal? 1/2 2/4)", "#t"},
		{"equal? with characters", "(equal? #\\a #\\a)", "#t"},
		{"equal? with strings", "(equal? \"hello\" \"hello\")", "#t"},
		{"equal? with lists", "(equal? '(1 2 3) '(1 2 3))", "#t"},
		{"equal? with nested lists", "(equal? '((1 2) (3 4)) '((1 2) (3 4)))", "#t"},

		// --- Behavior with Procedures (Unspecified by R4RS) ---
		// The standard does not specify the result of comparing procedures.
		// We test that the call succeeds and returns a boolean (either #t or #f).
		{"eq? with same builtin procedure", "(or (eq? + +) (not (eq? + +)))", "#t"},
		{"eq? with different builtin procedures", "(or (eq? + -) (not (eq? + -)))", "#t"},
		{"eqv? with same builtin procedure", "(or (eqv? + +) (not (eqv? + +)))", "#t"},
		{"eqv? with different builtin procedures", "(or (eqv? + -) (not (eqv? + -)))", "#t"},
		{"equal? with same builtin procedure", "(or (equal? + +) (not (equal? + +)))", "#t"},
		{"equal? with different builtin procedures", "(or (equal? + -) (not (equal? + -)))", "#t"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("For input %q, expected %q, got %q", tt.input, tt.expected, result.Inspect())
			}
		})
	}
}